package codegenjvm;

import java.util.ArrayList;
import java.util.Stack;

import ast.ASTNode;
import ast.Expr;

import util.ClassTreeNode;
import util.SymbolTable;

public class VirtualCodeGenEnv {

	
	private int maxOpSize;
	private int maxLocSize;
	
	private Stack<String> opStack;
	private ArrayList<ArrayList<BullShit>> locList;
	
	public VirtualCodeGenEnv() {
		opStack = new Stack<String>();
		locList = new ArrayList<ArrayList<BullShit>>();
		maxOpSize = 0;
		maxLocSize = 0;
	}
	
	
	void push(String var) {
		opStack.push(var);
		int currOpSize = opStack.size();
		if(maxOpSize < currOpSize) maxOpSize = currOpSize;
		System.out.println("OP  : " + opStack);
	}
	String pop() {
		String popped = opStack.pop();
		return popped;
	}
	
	void addLocal(String name, String type) {
		locList.get(locList.size()-1).add(new BullShit(name,type));
		int currLocSize = getLocOffsetOfScope(locList.size());
		if(maxLocSize < currLocSize) maxLocSize = currLocSize;
		System.out.println("LOC : " + locList);
	}
	
	
	int getMaxOpSize() {
		return maxOpSize;
	}
	int getMaxLocSize() {
		return maxLocSize;
	}
	
	
	String getLocalType(String var, boolean isField) {
		String type = "";
		if(!isField) {
			int j;
			for(int i = locList.size()-1; i >= 1 ; i--) {
				for(j = 0; j < locList.get(i).size(); j++) {
					if(locList.get(i).get(j).getShityName().equals(var)) {
						type = locList.get(i).get(j).getShityType();
						break;
					}
				}
				if(locList.get(i).get(j).getShityName().equals(var)) {
					break;
				}
			}
		} else {
			for(int i = locList.get(0).size()-1; i >= 0; i--) {
				if(locList.get(0).get(i).getShityName().equals(var)) {
					type = locList.get(0).get(i).getShityType();
					break;
				}
			}
		}
		return type;
	}
		
	int getLocalOffset(String var, boolean isField) {
		int i = 0, offset = 0;
		if(isField) {
			for(;i<locList.get(0).size()-1;i++) {
				if(locList.get(0).get(i).getShityName().equals(var)) break;
			}
			offset = i;
		} else {
			for(i = locList.size()-1; i >= 1 ; i--) {
				for(offset = 0; offset < locList.get(i).size(); offset++) {
					if(locList.get(i).get(offset).getShityName().equals(var)) {
						break;
					}
				}
				if(locList.get(i).get(offset).getShityName().equals(var)) {
					break;
				}
			}
			System.out.println("Offset : " + offset);
			System.out.println("Scope : " + i + "/" + locList.size());
			offset = offset + getLocOffsetOfScope(i);
		}
		return offset;
	}
	
	String getMostRecentLocal() {
		return locList.get(locList.size()-1).get(
				locList.get(locList.size()-1).size()-1).getShityName();
	}
	
	boolean isField(String var) {
		int i; 
		for(i= locList.size()-1; i <= 0; i--) {
			if(locList.get(i).contains(var)) break;
		}
		// Index Zero is field scope
		return (i == 0) ? true : false;
	}
	
	/******** Popping  Helper Methods ***********/
	
	boolean stackIsEmpty()
	{
		return (opStack.size() == 0) ? true : false;
	}
	
	int getCurrOpSize() {
		return opStack.size();
	}
	
	int getCurrLocalSize() {
		return locList.get(locList.size()-1).size();
	}
	
	/********************************************/
	private int getLocOffsetOfScope(int scope) {
		int size = 0;
		for(int i = 1; i < scope; i++) {
			size += locList.get(i).size();
		}
		System.out.println("Local Size : " + size);
		return size + 1;
	}
	
	void enterNewMethod()
	{
		enterScope();
		maxOpSize = 0;
		maxLocSize = getLocOffsetOfScope(locList.size());
	}
	void exitNewMethod() {
		exitScope();
		if(locList.size() != 1) {
			System.err.println("Error: Not at Field Scope");
		}
	}
	void enterScope() {
    	locList.add(new ArrayList<BullShit>());
    }
	
	void exitScope() {
		if(locList.size() == 1) {
			System.err.println("Error: Fields are being popped");
		} else {
			locList.remove(locList.size()-1);
			//Prepare for next method locals and operands
			if(locList.size() == 1) {
				maxOpSize = 0;
				maxLocSize = 0;
			}
		}
	}
	private class BullShit
	{
		private String shityName, shityType;
		public BullShit(String name, String type)
		{
			shityName = name;
			shityType = type;
		}
		String getShityType()
		{
			return shityType;
		}
		String getShityName()
		{
			return shityName;
		}
		public String toString()
		{
			return shityName + ":" + shityType;
		}
	}
}
